#include <linux/module.h>
#include <linux/pci.h>
#include "drv_aom.h"
#include "drv_pcie.h"
#include "drv_cdev.h"
#include "drv_ao.h"
#include "drv_xcan.h"

#if (HARD_VER == 0)
#define AOM_AO_DEV_NUM          12                  /* AO 设备数量 */
#define AOM_CANFD_DEV_NUM       1                   /* CANFD 设备数量 */
#elif (HARD_VER == 1)
#define AOM_AO_DEV_NUM          12                  /* AO 设备数量 */
#define AOM_CANFD_DEV_NUM       2                   /* CANFD 设备数量 */
#endif
#define AOM_AO_DRV_NAME         "pcie_ao"           /* AO 设备名称 */
#define AOM_CANFD_DRV_NAME      "pcie_canfd"        /* CANFD 设备名称 */

struct cdev_dev_t aom_ao_cdev[AOM_AO_DEV_NUM];      /* AO 模块 AO 字符设备 */
struct ao_dev_t aom_ao_dev[AOM_AO_DEV_NUM];         /* AO 模块 AO 设备 */

/* PCIe AO 模块 */
struct pcie_aom_t {
    struct device *dev;             /* Linux 设备指针 */
    struct pcie_dev_t pcie_dev;     /* PCIe 设备 */
    struct ao_ctrl_t ao_ctrl;       /* AO 控制器 */
    struct xcan_ctrl_t xcanfd_ctrl;     /* CANFD 控制器 */
};

static int pcie_aom_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
    int ret = 0;
    struct pcie_aom_t *pmcdev = NULL;

    dev_info(&pdev->dev, "Probing %04x:%04x", pdev->vendor, pdev->device);
    /* 检查设备是否已被占用 */
    if (pci_get_drvdata(pdev)) {
        dev_warn(&pdev->dev, "device already claimed");
        return -EBUSY;
    }

    /* 分配设备结构内存 */
    pmcdev = devm_kzalloc(&pdev->dev, sizeof(*pmcdev), GFP_KERNEL);
    if (!pmcdev)
        return -ENOMEM;

    pmcdev->dev = &pdev->dev;
    pci_set_drvdata(pdev, pmcdev);

    /* 初始化 PCIe 设备 */
    ret = init_pcie_dev(pdev, &pmcdev->pcie_dev);
    if (ret) {
        dev_err(pmcdev->dev, "Init PCIe device failed");
        goto err_cleanup;
    }

    /* 初始化 AO 控制器 */
    ret = init_ao_ctrl(&pmcdev->pcie_dev, aom_ao_cdev, &pmcdev->ao_ctrl, 
                       aom_ao_dev, AOM_AO_DRV_NAME, AOM_AO_DEV_NUM);
    if (ret < 0) {
        dev_err(pmcdev->dev, "Init AO controller failed");
        goto err_cleanup;
    }
    dev_info(pmcdev->dev, "Init AO controller success");
    dev_info(&pdev->dev, "Probe PCIe %s device success", AOM_DRV_NAME);

    return 0;

err_cleanup:
    pci_set_drvdata(pdev, NULL);

    return ret;
}

static void pcie_aom_remove(struct pci_dev *pdev)
{
    struct pcie_aom_t *pmcdev = pci_get_drvdata(pdev);

    if (!pmcdev) {
        dev_warn(&pdev->dev, "No device data found");
        return;
    }
    dev_info(&pdev->dev, "Removing PCIe %s device", AOM_DRV_NAME);

    /* 注销 AO 控制器 */
    exit_ao_ctrl(&pmcdev->ao_ctrl, AOM_AO_DEV_NUM);
    dev_info(pmcdev->dev, "Exit %s controller success", pmcdev->ao_ctrl.name);

    /* 注销 PCIe 设备 */
    exit_pcie_dev(pdev, &pmcdev->pcie_dev);
    dev_info(&pdev->dev, "Exit %s device success", pmcdev->pcie_dev.name);

    pci_set_drvdata(pdev, NULL);
    dev_info(&pdev->dev, "Remove PCIe %s device success", AOM_DRV_NAME);
}

/* PCI 设备 ID 表 */
static const struct pci_device_id pcie_aom_ids[] = {
    { PCI_DEVICE(0x594D, 0x0005) },     /* ao module */
    { 0, }
};
MODULE_DEVICE_TABLE(pci, pcie_aom_ids);

/* PCI AO 驱动结构 */
struct pci_driver pcie_aom_driver = {
    .name = AOM_DRV_NAME,
    .id_table = pcie_aom_ids,
    .probe = pcie_aom_probe,
    .remove = pcie_aom_remove,
};
